﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sherwood.SignOn.Server.Models.Repositories;

namespace Sherwood.SignOn.Server.Models.Repositories.Sql
{
    public class UserAccountRepository : IUserAccountRepository
    {
        public virtual IUserAccount AuthenticateUser(string username, string password)
        {
            SqlRepositoryDataContext db = new SqlRepositoryDataContext();
            username = username == null ? "" : username.Trim().ToLower();
            IUserAccount user = (from a in db.UserAccounts where (a.IsEnabled == true && a.UserName != null && a.UserName.ToLower() == username) select a).FirstOrDefault();

            if (user != null)
            {
                if (user.VerifyPassword(password))
                {
                    return user;
                }
            }
            db.Dispose();
            return null;
        }

        public virtual IUserAccount GetUserAccount(Guid userAccountId)
        {
            SqlRepositoryDataContext db = new SqlRepositoryDataContext();
            IUserAccount user = (from a in db.UserAccounts where a.Id.Equals(userAccountId) select a).FirstOrDefault();
            db.Dispose();
            return user;
        }

        public virtual IUserAccount GetUserAccountByEmail(string email)
        {
            SqlRepositoryDataContext db = new SqlRepositoryDataContext();
            email = email.ToLower() ?? "";
            IUserAccount user = (from a in db.UserAccounts where a.Email != null && a.Email.ToLower() == email select a).FirstOrDefault();
            db.Dispose();
            return user;
        }

        public virtual List<IUserAccount> GetAllUserAccountsByEmail(string email)
        {
            SqlRepositoryDataContext db = new SqlRepositoryDataContext();
            email = email != null ? email.ToLower() : "";
            List<IUserAccount> users = new List<IUserAccount>((from a in db.UserAccounts where a.Email != null && a.Email.ToLower() == email select a));
            db.Dispose();
            return users;
        }

        public virtual IUserAccount GetUserAccountByUsername(string username)
        {
            SqlRepositoryDataContext db = new SqlRepositoryDataContext();
            username = username != null ? username.ToLower() : "";
            IUserAccount user = (from a in db.UserAccounts where a.UserName != null && a.UserName.ToLower() == username select a).FirstOrDefault();
            db.Dispose();
            return user;
        }

        public virtual IUserAccount CreateUserAccount(CreateUserAccountParams parameters)
        {
            SqlRepositoryDataContext db = new SqlRepositoryDataContext();
            UserAccount account = null;
            if (parameters.UserAccountId != Guid.Empty)
            {
                account = (from a in db.UserAccounts where a.Id.Equals(parameters.UserAccountId) select a).FirstOrDefault();
            }
            bool isNewAccount = account == null;
            if (account == null)
            {
                account = new UserAccount();
                account.Id = Guid.NewGuid();
            }
            account.RequestedUrl = parameters.RequestedUrl;
            account.DateCreated = DateTime.Now;
            account.DateModified = DateTime.Now;
            account.UserName = parameters.UserName;
            account.DateOfBirth = parameters.DateOfBirth;
            account.Email = parameters.Email;
            account.Mobile = parameters.Mobile;
            account.Gender = parameters.Gender;
            account.CultureName1 = parameters.CultureName1;
            account.CultureName2 = parameters.CultureName2;
            account.CultureName3 = parameters.CultureName3;
            account.Country = parameters.Country;
            account.FirstName = parameters.FirstName;
            account.LastName = parameters.LastName;
            account.IsEnabled = parameters.IsEnabled;
            account.VerificationStatus = parameters.VerificationStatus;

            if (isNewAccount)
                db.UserAccounts.InsertOnSubmit(account);
            db.SubmitChanges();
            account.SetPassword(parameters.Password);
            db.Dispose();

            if (isNewAccount)
            {
                account.CreateVerificationCode();
            }

            return account;
        }

        /// <summary>
        /// Override to allow duplicate user accounts to be created in certain instances.
        /// </summary>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public virtual bool AllowDuplicateEmailAddress(CreateUserAccountParams parameters)
        {
            return false;
        }


        public IUserAccount GetUserAccountByUsernameAndEmail(string username, string email)
        {
            SqlRepositoryDataContext db = new SqlRepositoryDataContext();
            username = username != null ? username.ToLower() : "";
            email = email != null ? email.ToLower() : "";
            IUserAccount user = (from a in db.UserAccounts where a.UserName != null && a.UserName.ToLower() == username && a.Email != null && a.Email.ToLower() == email select a).FirstOrDefault();
            db.Dispose();
            return user;
        }

        public IUserAccount GetUserAccountByUsernameOrEmail(string username, string email)
        {
            if (!string.IsNullOrEmpty(username) || !string.IsNullOrEmpty(email))
            {
                username = username != null ? username.ToLower() : "";
                email = email != null ? email.ToLower() : "";
                SqlRepositoryDataContext db = new SqlRepositoryDataContext();
                IUserAccount user = (from u in db.UserAccounts where (!(u.Email == null || u.Email.Length == 0) && u.Email.ToLower() == email) || (!(u.UserName == null || u.UserName.Length == 0) && u.UserName.ToLower() == username) select u).FirstOrDefault();
                db.Dispose();
                return user;
            }
            return null;
        }

        public IUserAccount ModifyUserAccount(CreateUserAccountParams parameters)
        {
            SqlRepositoryDataContext db = new SqlRepositoryDataContext();
            UserAccount account = null;
            if (parameters.UserAccountId != Guid.Empty)
            {
                account = (from a in db.UserAccounts where a.Id.Equals(parameters.UserAccountId) select a).FirstOrDefault();
            }
            if (account == null)
            {
                return null;
            }

            account.RequestedUrl = parameters.RequestedUrl;
            //account.DateCreated = parameters.DateCreated;
            account.DateModified = DateTime.Now;
            account.UserName = parameters.UserName;
            account.DateOfBirth = parameters.DateOfBirth;
            //account.Email = parameters.Email; (email address changes not allowed without verification)
            account.Mobile = parameters.Mobile;
            account.Gender = parameters.Gender;
            account.CultureName1 = parameters.CultureName1;
            account.CultureName2 = parameters.CultureName2;
            account.CultureName3 = parameters.CultureName3;
            account.Country = parameters.Country;
            account.FirstName = parameters.FirstName;
            account.LastName = parameters.LastName;

            db.SubmitChanges();
            db.Dispose();

            return account;
        }

        public IEnumerable<IUserAccount> GetAllAccounts()
        {
            SqlRepositoryDataContext db = new SqlRepositoryDataContext();
            List<UserAccount> userAccounts = db.UserAccounts.ToList();
            db.Dispose();
            return userAccounts;
        }

        public void DeleteAllAccounts()
        {
            SqlRepositoryDataContext db = new SqlRepositoryDataContext();
            db.ExecuteCommand("DELETE FROM UserSessionClientRecord");
            db.ExecuteCommand("DELETE FROM UserSession");
            db.ExecuteCommand("DELETE FROM UserAccount");
            db.SubmitChanges();
            db.Dispose();
        }


        public IEnumerable<IUserAccount> GetUserAccounts(string search)
        {
            search = search != null ? search.Replace(",", "").Replace("\n", " ").Replace("\r", "").Replace("\t", " ").Trim().ToLower() : "";
            if (string.IsNullOrEmpty(search))
                return new List<IUserAccount>(); //return empty list

            List<IUserAccount> sortedResults = null;

            SqlRepositoryDataContext db = new SqlRepositoryDataContext();
            if (!search.Contains(" "))
            {
                sortedResults = new List<IUserAccount>((from a in db.UserAccounts where a.UserName.ToLower().Contains(search) || a.Email.ToLower().Contains(search) || a.FirstName.ToLower().Contains(search) || a.LastName.ToLower().Contains(search) select a).AsEnumerable());
            }
            else
            {
                string[] parts = search.Split(' ');
                string[] simpleParts = new string[parts.Length];
                for (int i = 0; i < parts.Length; i++)
                    simpleParts[i] = StringHelper.RemoveDiacritics(parts[i]);

                Dictionary<Guid, IUserAccount> resultList = new Dictionary<Guid, IUserAccount>();
                foreach (string part in simpleParts)
                {
                    if (part != "")
                    {
                        //Coarse filter based on one one segment of a space seperated search string
                        IEnumerable<IUserAccount> partlist = (from a in db.UserAccounts where a.UserName.ToLower().Contains(part) || a.Email.ToLower().Contains(part) || a.FirstName.ToLower().Contains(part) || a.LastName.ToLower().Contains(part) select a).AsEnumerable();

                        //Refine result list by matching segements with multiple fields
                        foreach (IUserAccount user in partlist)
                        {
                            string userFirstName = StringHelper.RemoveDiacritics(user.FirstName).ToLower().Trim();
                            string userLastName = StringHelper.RemoveDiacritics(user.LastName).ToLower().Trim();
                            string userEmail = StringHelper.RemoveDiacritics(user.Email).ToLower().Trim();

                            if ((userFirstName.Contains(simpleParts[0]) && userLastName.Contains(simpleParts[1]))
                                || (userFirstName.Contains(simpleParts[1]) && userLastName.Contains(simpleParts[0]))
                                || (parts.Length > 2 && userFirstName.Contains(simpleParts[0]) && userLastName.Contains(simpleParts[simpleParts.Length - 1]))
                                || (parts.Length > 2 && userFirstName.Contains(simpleParts[simpleParts.Length - 1]) && userLastName.Contains(simpleParts[0]))
                                || (userEmail.Contains(simpleParts[0]) && userEmail.Contains(simpleParts[1]))
                                || (userEmail.Contains(simpleParts[0]) && userLastName.Contains(simpleParts[1]))
                                || (userEmail.Contains(simpleParts[1]) && userLastName.Contains(simpleParts[0]))
                                || StringHelper.LooseMatch(search, user.FirstName + user.LastName)
                                || StringHelper.LooseMatch(search, user.LastName + user.FirstName)
                                )
                            {
                                if (!resultList.ContainsKey(user.Id))
                                    resultList.Add(user.Id, user);
                            }
                        }
                    }
                }
                sortedResults = resultList.Values.ToList();
            }
            sortedResults.Sort((a, b) => (a.LastName + a.FirstName).CompareTo(b.LastName + a.FirstName));
            db.Dispose();
            return sortedResults;
        }


        public bool DeleteUserAccount(Guid userAccountId)
        {
            SqlRepositoryDataContext db = new SqlRepositoryDataContext();
            UserAccount account = (from a in db.UserAccounts where a.Id.Equals(userAccountId) select a).FirstOrDefault();
            if (account == null)
            {
                db.Dispose();
                return false;
            }
            else
            {
                foreach (UserSession session in account.UserSessions)
                {
                    db.UserSessionClientRecords.DeleteAllOnSubmit(session.UserSessionClientRecords);
                    db.UserSessions.DeleteOnSubmit(session);
                }
                db.UserAccounts.DeleteOnSubmit(account);
                db.SubmitChanges();
                db.Dispose();
                return true;
            }
        }
    }
}
